// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
//!  This example shows you how to define ops in Rust and then call them from
//!  JavaScript.

use alice2d::math::Vector2f;
use deno_core::error::AnyError;
use deno_core::op;
use deno_core::Extension;
use deno_core::JsRuntime;
use deno_core::RuntimeOptions;

// This is a hack to make the `#[op]` macro work with
// deno_core examples.
// You can remove this:
use deno_core::*;
use deno_core::v8::Function;
use deno_core::v8::HandleScope;
use deno_core::v8::Local;
use deno_core::v8::Object;
use deno_core::v8::Value;




#[derive(Debug,serde::Serialize,serde::Deserialize)]
pub struct  Demo {
  name:String
}

#[op]
fn op_sum(vec2: Vector2f ) -> Result<Vector2f, AnyError> {
  // Sum inputs
  let result = vec2 * 2.0;

  // return as a Result<f64, AnyError>
  Ok((result))
}


fn main() {
  let alice2d_ext = Extension::builder()
  .js(include_js_files!(
      prefix "alice:2d",
      "alice.js",
  ))
  .ops(vec![
    // An op for summing an array of numbers
    // The op-layer automatically deserializes inputs
    // and serializes the returned Result & value
    op_sum::decl(),
  ])
  .build();
// Initialize a runtime instance
let mut runtime = JsRuntime::new(RuntimeOptions {
  extensions: vec![alice2d_ext],
  ..Default::default()
});

runtime
.execute_script(
  "<usage>",
  r#"
// Print helper function, calling Deno.core.print()
function print(value) {
Deno.core.print(value.toString()+"\n");
}

class Player extends Alice.Entity {
    constructor(){
      super()
      this.speed = 15.0
    }
}

"#,
)
.unwrap();


let code = include_str!("ClassMember.js");

let js_code = format!(
  "{{
      {c}
      new {f}()
      }}",
  c = code,
  f = "ClassMember",

);
let res = runtime.execute_script("<anon>", &js_code);

match res {
  Ok(global) => {
      {
        let scope = &mut runtime.handle_scope();

        let id      = v8::String::new(scope, "uuid-uid").unwrap();
        let holder = v8::Local::new(scope, global.clone());

        if holder.is_object() {
          let holder = holder.to_object(scope).unwrap();

          let props = holder.get_own_property_names(scope, Default::default()).unwrap();

          for i in 0..props.length() {
            let key = props.get_index(scope, i).unwrap();
            let deserialized_key =
              serde_v8::from_v8::<serde_json::Value>(scope, key).unwrap();

              let value = holder.get(scope, key).unwrap();

     

              let deserialized_value =
              serde_v8::from_v8::<serde_json::Value>(scope, value).unwrap();

              // println!("deserialized_key={:?}",deserialized_key);
              println!("deserialized_value={:?}",deserialized_value);
              
          }

        }
     
        try_invoke_js_function(scope, holder, "onCreate", &[id.into()]);

      }

    
  }

  Err(e) => panic!("get script instance failed! "),
}
  // Now we see how to invoke the op we just defined. The runtime automatically
  // contains a Deno.core object with several functions for interacting with it.
  // You can find its definition in core.js.
  
}



pub fn try_invoke_js_function(
  scope: &mut HandleScope,
  holder: Local<Value>,
  name: &str,
  args: &[Local<Value>],
) {

  if holder.is_object() {
      let obj = holder.to_object(scope).unwrap();
      let func_name = deno_core::v8::String::new(scope, name).unwrap();
      let func = obj.get(scope, func_name.into()).unwrap();
      if func.is_function() {
          let func = v8::Local::<v8::Function>::try_from(func)
              .expect(&format!("获取脚本 {} 方法失败", name));

          func.call(scope, obj.into(), args);
      }
  }
}